<template>
  <DragResize
    v-show="isShow"
    v-bind="ctrl.dragOptions"
    ref="dnr"
    :class="[
      'fa-widget',
      `fa-widget-${data.option.key}`,
      data.className,
      customClassName,
      `widget-mode-${data.mode}`,
      `actor-${data.actorColor}`,
      {
        'is-riding': data.isRiding,
        'is-batch': data.isBatch,
        'is-widget-active': active || isSelected,
        'is-error': !!error.message,
        'is-error-marked': error.isMarket
      }
    ]"
    :data="ctrl.dragData"
    :widget-id="data.id"
    :tabindex="tabIndex"
    :on-dragging="ctrl.dragging"
    :on-resizing="ctrl.resizing"
    @drag-start="ctrl.dragStart"
    @drag-stop="ctrl.dragStop"
    @resize-stop="ctrl.resizeStop"
    @mouseenter.native="hovering = true"
    @mouseleave.native="hovering = false"
    @click="onClick"
    @focus.native="activate"
  >
    <WidgetMenu :data="data" :menu="menu" :disabled="!showMenu">
      <!-- 清除 tabindex 避免 ElDropdown 导致上下键移动控件失效 -->
      <div class="fa-widget__box" :tabindex="undefined">
        <slot />

        <div v-if="showRequired" class="fa-widget__required">*</div>

        <!-- 错误提示 -->
        <WidgetLabel v-if="error.message" :visible="hovering || error.visible" :label="error.message" error />

        <!-- 控件 label 展示 -->
        <WidgetLabel v-else :visible="hovering" :label="data.label" />

        <!-- 控件提示 -->
        <WidgetTips v-if="tips" :tips="tips" />

        <!-- 批量提示 -->
        <Transition name="fa-widget__fade">
          <div v-if="batchTips" v-show="hovering" class="fa-widget__batch-tips">{{ batchTips }}</div>
        </Transition>

        <!-- 控件标签 -->
        <Tag class="fa-widget-tag" v-if="tag && tag.text" v-bind="tag" size="mini" />

        <!-- FaContract 控件内容插槽 -->
        <Component v-if="widgetSlot" :is="widgetSlot" />

        <Transition name="fa-widget__fade">
          <a v-if="showCloseIcon" class="fa-widget__close el-icon-error" @click.stop="onClose" @mousedown.stop></a>
        </Transition>
      </div>
    </WidgetMenu>
  </DragResize>
</template>

<script lang="ts">
import { defineComponent, toRefs, PropType, computed, ref, inject, watch, onMounted, onBeforeMount } from 'vue'
import { debounce } from 'lodash'
import { Widget } from '@/models'
import { WidgetMode, WidgetMenuItem, Viewer, ContractPage, TagProps } from '@/types'

import useWidgetState from './useWidgetState'
import useWidgetCtrl from './useWidgetCtrl'
import useWidgetError from './useWidgetError'

import DragResize from '../drag-resize/DragResize.vue'
import WidgetLabel from './WidgetLabel.vue'
import WidgetTips from './WidgetTips.vue'
import Tag from '../tag/Tag.vue'
import WidgetMenu from './WidgetMenu.vue'

/** 基础控件 */
export default defineComponent({
  name: 'FaWidgetBase',

  components: { DragResize, WidgetLabel, WidgetTips, Tag, WidgetMenu },

  props: {
    /** 控件数据 */
    data: {
      type: Widget,
      required: true
    },

    /** 菜单 */
    menu: {
      type: Array as PropType<WidgetMenuItem[]>,
      default: () => []
    },

    /** 拖拽父元素 */
    parent: {
      type: [Boolean, String],
      default: undefined
    },

    /** 是否允许拖拽 */
    draggable: {
      type: Boolean,
      default: true
    },

    /**
     * 是否允许清理
     * @description
     * 在使用模式下，点击关闭按钮将清理控件值
     */
    clearable: {
      type: Boolean,
      default: false
    }
  },

  emits: [
    /**
     * 点击事件 `(e: MouseEvent) => void`
     * @description 避免使用原生 click 在拖拽结束后也会触发
     */
    'click',

    /** 拖拽开始 */
    'drag-start',

    /** 拖拽中 */
    'dragging',

    /** 拖拽结束 */
    'drag-stop'
  ],

  setup(props, { emit }) {
    /** 合同控件 */
    const viewer = inject('Viewer') as Viewer

    /** 页面 */
    const Page = inject('ContractPage') as ContractPage

    /** 拖拽和调整大小 */
    const dnr = ref(null as null | InstanceType<typeof DragResize>)

    const { data, draggable, parent } = toRefs(props)

    /** 页码 */
    const p = computed(() => Page.page.p)

    /** 控件状态 */
    const state = useWidgetState(data)

    /** 控件错误 */
    const error = useWidgetError(data)

    /** 是否允许清理 */
    const clearable = computed(() => props.data.isUse && props.clearable)

    /** 批量提示 */
    const batchTips = computed(() => {
      const widget = props.data

      if (widget.isDone || !(widget.isBatch || isRidingBatch())) return

      return widget.isBatch
        ? `${widget.linkDocs.length}份文档；${widget.batchApplyCount}处位置`
        : `${widget.linkDocs.length}份文档`
    })

    /** 标签 */
    const tag = computed(() => {
      const widget = props.data

      if (widget.isDone) return

      let tag = widget.signTag || viewer.signOptions.signTag?.(widget) || getDefaultTag(widget)

      if (typeof tag === 'string') return { text: tag }

      return tag
    })

    /** 默认标签 */
    function getDefaultTag(widget: Widget): TagProps | string | undefined {
      if (widget.assignedSignId) {
        return {
          text: '指定章',
          effect: 'normal'
        }
      }

      if (widget.isBatch || isRidingBatch())
        return {
          text: '批量',
          effect: 'normal',
          tagClass: 'actor-theme'
        }

      return
    }

    /** 是否骑缝批量应用 */
    function isRidingBatch() {
      const widget = props.data
      return widget.isRiding && widget.linkDocs.length > 1
    }

    /** 更新激活 */
    function updateActive() {
      const dnrVm = dnr.value
      if (dnrVm) dnrVm.state.active = state.active
    }

    watch(
      () => state.active,
      active => {
        updateActive()

        /** 控件失去聚焦时触发校验 */
        !active && error.trigger()
      }
    )

    /** 是否拖拽中 */
    const dragging = computed(() => dnr.value?.move.isMoving || false)

    /**
     * 控件拖拽时设置当前页为激活页
     * 页面激活比 hover 拥有更高层级 index
     * 避免跨页拖动太快控件被页面遮挡
     */
    watch(dragging, dragging => {
      if (dragging) {
        viewer.state.pageIndex = p.value
      }
    })

    /** 来源配置: 清理无效来源配置 */
    watch(
      () => props.data.sourceItem,
      item => {
        const widget = props.data

        // 无匹配数据来源时，清理无效的来源配置
        if (!item && widget.source) {
          widget.source = undefined
        }
      },
      { immediate: true }
    )

    /** 来源配置：改变数据来源后清理默认值 */
    watch(
      () => props.data.sourceItem,
      item => {
        const widget = props.data

        // 设置模式指定数据来源后清理默认值
        if (item && widget.isSet) {
          widget.value = ''
        }
      }
    )

    /** 来源配置：参与方改变后清理数据来源的填写值 */
    watch(
      () => props.data.actor,
      item => {
        const widget = props.data

        // 清理数据来源的填写值
        if (widget.sourceItem && widget.value) {
          widget.value = ''
        }
      }
    )

    /** 控件信息变更同步关联控件 */
    watch(
      () => {
        const { actor, applyPage, x, y, w, h, sign, followSignSize, keepSignAspect } = props.data
        return { actor, applyPage, x, y, w, h, sign, followSignSize, keepSignAspect }
      },
      debounce(() => {
        const widget = props.data

        if (!widget.linkId) return

        widget.syncLinkWidgets()
      }, 100)
    )

    /** 挂载前绑定合同控件 */
    onBeforeMount(() => {
      props.data.viewer = viewer
    })

    onMounted(() => {
      const widget = props.data

      !widget.isFixed && widget.adjust()

      updateActive()
    })

    return {
      ...toRefs(state),

      /** 拖拽和调整大小 */
      dnr,

      /** 控件错误 */
      error,

      /** 提示信息 */
      tips: computed(() => {
        const widget = props.data
        return widget.tips || viewer.widgetTips?.(widget)
      }),

      /** 批量提示 */
      batchTips,

      /** 标签 */
      tag,

      /** 控件拖拽控制 */
      ctrl: useWidgetCtrl(data, draggable, parent, emit),

      /** 是否选中 */
      isSelected: computed(() => viewer.selection.list.includes(props.data)),

      /** 聚焦索引 */
      tabIndex: computed(() => {
        const widget = props.data
        // 骑缝控件非第一页不添加聚焦索引
        return widget.isRiding && p.value > 0 ? undefined : viewer.sortedWidgets.indexOf(widget) + 1
      }),

      /** 是否展示关闭按钮 */
      showCloseIcon: computed(() => (state.hovering || state.active) && (props.data.removable || clearable.value)),

      /** 是否显示工具栏 */
      showMenu: computed(
        () => [WidgetMode.SET, WidgetMode.USE].includes(props.data.mode) && !!props.menu?.length && !dragging.value
      ),

      /** FaContract 控件内容插槽 */
      widgetSlot: computed(() => {
        const slot = viewer.$scopedSlots['widget']

        if (!slot) return

        return defineComponent({
          render: h => {
            const nodes = slot({ widget: props.data })

            if (!nodes) return

            return nodes.length > 1 ? h('div', nodes) : nodes?.[0]
          }
        })
      }),

      /** 控件点击 */
      onClick(e: MouseEvent) {
        const widget = props.data

        // Ctrl/Cmd 点击多选
        if (e.ctrlKey || e.metaKey) {
          e.stopPropagation()

          // 排除固定的控件和骑缝控件
          if (widget.isFixed || widget.isRiding) return

          const { lastActiveWidget, selection } = viewer

          selection.toggle(widget)

          if (lastActiveWidget && lastActiveWidget !== widget) {
            selection.add(lastActiveWidget)
          }

          viewer.activeWidget = undefined
        } else {
          emit('click', e)

          viewer.$emit('widget-click', widget, e)
        }
      },

      /** 关闭、清理按钮操作 */
      onClose() {
        if (props.data.removable) {
          viewer.removeWidget(props.data, false, true)
        } else if (clearable.value) {
          props.data.clear()
        }
      }
    }
  }
})
</script>

<style lang="scss">
// 控件
.fa-widget {
  position: absolute;
  z-index: 1;
  box-sizing: border-box;
  line-height: 1.5;
  border: none;

  &.is-active,
  &.is-error {
    z-index: 2;
  }

  &.is-error-marked,
  &.is-error.is-active {
    z-index: 3;
  }

  &:hover,
  &:focus {
    z-index: 4;
  }

  &:focus {
    outline: none;
  }

  // 框选中
  &.is-selecting,
  &.is-widget-active {
    .fa-widget__box {
      border-style: solid;
    }
  }

  // 激活
  &.is-widget-active {
    div.fa-widget__box,
    div.fa-widget__box:hover {
      background-color: #fff;
    }
  }

  // 错误
  &.is-error-marked {
    .fa-widget__box {
      border: 1px solid $color-danger !important;
      border-radius: 0;
      box-shadow: 0 0 6px 0 rgba($color-danger, 0.3) !important;

      &::after {
        position: absolute;
        top: -1px;
        left: -1px;
        z-index: 1;
        border: 4px solid transparent;
        border-top-color: $color-danger;
        border-left-color: $color-danger;
        border-radius: 2px;
        content: '';
      }
    }

    .handle {
      display: none;
    }
  }

  // resize 句柄
  & > .handle {
    $offset: -2px;

    &-tl {
      top: $offset;
      left: $offset;
    }

    &-bl {
      bottom: $offset;
      left: $offset;
    }

    &-br {
      right: $offset;
      bottom: $offset;
    }
  }
}

// 控件盒子
.fa-widget__box {
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  border: 1px dashed;
  border-radius: 2px;
  transition: border-color 0.2s ease-in-out, background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

// 参与方颜色样式
@each $c, $color in $actor-colors {
  .fa-widget.actor-#{$c} {
    .fa-widget__box {
      background-color: rgba($color, 0.03);
      border-color: rgba($color, 0.6);

      &:hover {
        background-color: rgba(mix($color, #fff, 4%), 0.95);
        border-color: $color;
      }
    }

    &.is-selecting,
    &.is-widget-active {
      .fa-widget__box {
        border-color: $color;
      }
    }

    &.is-dragging,
    &.is-resizing {
      .fa-widget__box,
      .fa-widget__box:hover {
        background-color: rgba($color, 0.03);
        border-color: rgba($color, 0.6);
      }
    }

    &.widget-mode-done {
      // 完成模式
      .fa-widget__box {
        background-color: transparent;
        border-color: transparent;

        &:hover {
          background-color: rgba($color, 0.03);
          border-color: $color;
        }
      }

      &.is-widget-active .fa-widget__box {
        background-color: rgba($color, 0.03);
        border-color: $color;
      }
    }

    &.is-widget-active .fa-widget__box {
      box-shadow: 0 0 6px 0 rgba($color, 0.3);
    }

    .fa-dnr-handles .handle {
      background: $color !important;
    }

    .fa-widget-tag.actor-theme {
      background-color: mix($color, #fff, 80%);
    }
  }
}

// 关闭按钮
.fa-widget__close {
  $size: 20px;
  position: absolute;
  bottom: 100%;
  left: 100%;
  width: $size;
  height: $size;
  margin: 0 0 -5px -8px;
  color: $color-danger;
  font-size: $size;
  line-height: 1;
  background: #fff;
  border: 1px solid #fff;
  border-radius: 50%;
  cursor: pointer;
  transition: color 0.2s ease-in-out, opacity 0.5s ease-in-out;

  &:hover {
    color: $color-danger-light;
  }

  &:active {
    color: $color-danger-dark;
  }
}

.fa-widget__required {
  position: absolute;
  top: 0;
  right: 100%;
  margin-right: 5px;
  color: $color-danger;
  font-size: 18px;
  line-height: 1;
  pointer-events: none;
}

.fa-widget__batch-tips {
  $tips-color: rgba(0, 0, 0, 0.65);

  position: absolute;
  bottom: 100%;
  left: 0;
  margin-bottom: 8px;
  padding: 4px 8px;
  color: #fff;
  font-size: 12px;
  line-height: 1.5;
  white-space: nowrap;
  background: $tips-color;
  border-radius: 4px;

  &::after {
    position: absolute;
    top: 100%;
    left: 12px;
    margin-top: 0px;
    border: 5px solid transparent;
    border-top-color: $tips-color;
    content: '';
  }
}

/** 控件标签 */
.fa-widget-tag.fa-tag {
  position: absolute;
  top: -1px;
  left: -1px;
  border-radius: 1px 0 2px;

  &.actor-theme {
    color: #fff;
    opacity: 0.8;
  }
}
</style>
